using System;
using System.Text;
using System.Collections;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Windows.Forms;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace EasyPath_CSharp
{
    class EasyPathUtils
    {

        private const string PATH_NAME = "Path";


        private static EasyPathConfig getConfig()
        {
            var config = readEasyPathConfig();
            if (null == config)
            {
                MessageBox.Show("读取配置失败，请检查是否存在");
                return null;
            }

            return config;
        }

        public static void sync()
        {
            var config = getConfig();
            if(null == config)
            {
                return;
            }

            ICollection<string> c = new HashSet<string>();
            ICollection<string> s = new Dictionary<string, string>().Keys;

            // 获取系统环境变量
            var environment = RegistryUtils.list();
            dealEnvironment(environment, config);

            var configInclude = config.environment.include;

            var diff = DictionaryUtils.diff(environment, configInclude);
            dealMerge(diff);
            updateConfig(configInclude, diff);
            //printDiff(diff);

            var pathDiff = dealPath(config, environment, diff);

            // 环境变量更新到配合文件
            bool configChange = diff.leftOnly.Count > 0;
            if (pathDiff.leftOnly.Count > 0)
            {
                configChange = true;
                (config.path.include).UnionWith(pathDiff.leftOnly);
            }
            if (configChange)
            {
                removePath(config.environment.include);
                writeEasyPathConfig(config);
            }

            if (diff.rightOnly.Count > 0 || pathDiff.rightOnly.Count > 0)
            {
                // 本地配置独有值写入环境变量
                RegistryUtils.set(diff.rightOnly);
            }

            MessageBox.Show("同步成功");
        }

        /**
         * 值合并
         */
        private static void dealMerge(DictionaryDiff<string, string> diff)
        {
            // 处理需要合并的数据
            foreach (var kv in diff.merge)
            {
                var key = kv.Key;
                var leftValue = kv.Value.leftValue;
                var rightValue = kv.Value.rightValue;

                var value = keyMerge(key, leftValue, rightValue);
                if (string.IsNullOrEmpty(value))
                {
                    continue;
                }

                //Console.WriteLine($"keyMerge: {value}");
                if (value.Equals(leftValue))
                {
                    diff.leftOnly.Add(key, value);
                }
                else
                {
                    diff.rightOnly.Add(key, value);
                }
            }
        }

        /**
         * 更新本地配置
         */
        private static void updateConfig(IDictionary<string, string> configInclude, DictionaryDiff<string, string> diff)
        {
            // 环境变量独有值写入本地配置
            foreach (var kvp in diff.leftOnly)
            {
                string configValue;
                configInclude.TryGetValue(kvp.Key, out configValue);
                if (null == configValue)
                {
                    configInclude.Add(kvp.Key, kvp.Value);
                    continue;
                }

                configInclude[kvp.Key] = kvp.Value;
            }
        }

        private static CollectionDiff<string> dealPath(EasyPathConfig config, IDictionary<string, string> environment, DictionaryDiff<string, string> diff)
        {
            // 处理 Path 值
            var configPath = config.path;
            var paths = getPaths(environment, configPath);

            var pathDiff = CollectionUtils.diff(paths, configPath.include);

            // 生成最终的 path
            if (pathDiff.rightOnly.Count > 0)
            {
                var finalPath = getPath(environment) + ';' + string.Join(";", pathDiff.rightOnly);
                //Console.WriteLine($"finalPath: {finalPath}");

                diff.rightOnly.Add(PATH_NAME, finalPath);
            }

            return pathDiff;
        }

        /**
         * 打印解析出的数据
         */
        private static void printDiff(DictionaryDiff<string, string> diff)
        {
            Console.WriteLine($"leftOnly Count: {diff.leftOnly.Count}");
            foreach (var kv in diff.leftOnly)
            {
                Console.WriteLine($"leftOnly key: {kv.Key}, value: {kv.Value}");
            }

            Console.WriteLine($"rightOnly Count: {diff.rightOnly.Count}");
            foreach (var kv in diff.rightOnly)
            {
                Console.WriteLine($"rightOnly key: {kv.Key}, value: {kv.Value}");
            }
        }

        // 排除不需要操作的变量
        protected static void dealEnvironment(Dictionary<string, string> environment, EasyPathConfig config)
        {
            // 移除系统变量
            foreach (var key in config.environment.system)
            {
                environment.Remove(key);
                environment.Remove(key.ToUpper());
                environment.Remove(key.ToLower());
            }

            // 移除忽略变量
            foreach (var key in config.environment.exclude)
            {
                environment.Remove(key);
                environment.Remove(key.ToUpper());
                environment.Remove(key.ToLower());
            }
        }

        protected static string getPath(IDictionary<string, string> environment)
        {
            return environment[PATH_NAME];
        }

        // 处理不需要的 path 值
        protected static HashSet<string> getPaths(IDictionary<string, string> environment, PathConfig config)
        {
            var pathValue = getPath(environment);
            var paths = new HashSet<string>(pathValue.Split(';'));

            var configSystem = getUpperKeySet(config.system);
            var configExclude = getUpperKeySet(config.exclude);

            // 移除系统路径、忽略路径
            paths.RemoveWhere(
                path => string.IsNullOrEmpty(path) || configSystem.Contains(path.ToUpper()) || configExclude.Contains(path.ToUpper())
            );

            //Console.WriteLine("finish deal");
            //foreach (var path in paths)
            //{
            //    Console.WriteLine(path);
            //}

            return getMergeUpperKey(paths);
        }

        /*
         * 
         */
        protected static string keyMerge(string key, string systemValue, string backupValue)
        {
            DialogResult result = MessageBox.Show(
                $"选择“是”将保留：{systemValue}\n选择“否”将保留：{backupValue}\n取消将跳过此键"
                , $"键 {key} 的值存在冲突"
                , MessageBoxButtons.YesNoCancel
                , MessageBoxIcon.Question
            );

            switch (result)
            {
                case DialogResult.Yes:
                    return systemValue;

                case DialogResult.No:
                    return backupValue;
            }

            return string.Empty;
        }

        protected static EasyPathConfig readEasyPathConfig()
        {
            return JsonConvert.DeserializeObject<EasyPathConfig>(FileUtils.read());
        }

        protected static void writeEasyPathConfig(EasyPathConfig config)
        {

            var serializer = new JsonSerializer();
            TextReader tr = new StringReader(JsonConvert.SerializeObject(config));
            JsonTextReader jtr = new JsonTextReader(tr);
            object obj = serializer.Deserialize(jtr);

            StringWriter textWriter = new StringWriter();
            JsonTextWriter jsonWriter = new JsonTextWriter(textWriter)
            {
                Formatting = Formatting.Indented,
                Indentation = 4,
                IndentChar = ' '
            };
            serializer.Serialize(jsonWriter, obj);

            var configJson = textWriter.ToString();

            //Console.WriteLine("JSON string is:");
            //Console.WriteLine(configJson);

            FileUtils.write(configJson);
        }

        public static void backup()
        {
            var config = getConfig();
            if (null == config)
            {
                return;
            }

            var environment = RegistryUtils.list();
            dealEnvironment(environment, config);

            config.environment.include = environment;
            config.path.include = getPaths(environment, config.path);

            removePath(environment);

            writeEasyPathConfig(config);

            MessageBox.Show("备份成功");
        }

        public static void restore()
        {

            DialogResult result = MessageBox.Show(
                $"此操作将覆盖系统环境变量，配置文件数据错误可能导致系统出现故障！\n是否继续？"
                , $"将覆盖系统环境变量"
                , MessageBoxButtons.YesNo
                , MessageBoxIcon.Question
            );
            if(DialogResult.Yes != result)
            {
                return;
            }

            var config = getConfig();
            if (null == config)
            {
                return;
            }

            var environment = RegistryUtils.list();
            dealEnvironment(environment, config);

            var configInclude = config.environment.include;
            var diff = DictionaryUtils.diff(environment, configInclude);

            RegistryUtils.remove(diff.leftOnly.Keys);

            diff.rightOnly.Add(PATH_NAME, string.Join(";", getMergeUpperKey(config.path.system)) + ';' + string.Join(";", config.path.include));

            RegistryUtils.set(diff.rightOnly);

            MessageBox.Show("恢复成功");
        }

        private static HashSet<string> getMergeUpperKey(ICollection<string> keys)
        {

            var upperSet = new HashSet<string>();
            var normalSet = new HashSet<string>();

            // 分离全大写和普通字符串
            foreach (var key in keys)
            {
                if(key.Equals(key.ToUpper()))
                {
                    upperSet.Add(key);
                } else
                {
                    normalSet.Add(key);
                }
            }

            foreach(var key in normalSet)
            {
                var upperKey = key.ToUpper();
                if (upperSet.Contains(upperKey))
                {
                    upperSet.Remove(upperKey);
                }
            }
            normalSet.UnionWith(upperSet);

            //Console.WriteLine("getMergeUpperKey deal");
            //foreach (var path in normalSet)
            //{
            //    Console.WriteLine(path);
            //}

            return normalSet;
        }

        private static HashSet<string> getUpperKeySet(ICollection<string> keys)
        {
            var set = new HashSet<string>();

            foreach (var key in keys)
            {
                set.Add(key.ToUpper());
            }

            return set;
        }

        private static void removePath(IDictionary<string, string> dict)
        {
            if(dict.ContainsKey(PATH_NAME)) {
                dict.Remove(PATH_NAME);
            }
        }
    }
}
